package edu.unsw.comp9321.logic;

import edu.unsw.comp9321.jdbc.BookingDAO;
import edu.unsw.comp9321.jdbc.BookingDAOImpl;
import edu.unsw.comp9321.jdbc.BookingDTO;
import edu.unsw.comp9321.jdbc.CarDAO;
import edu.unsw.comp9321.jdbc.CarDAOImpl;
import edu.unsw.comp9321.jdbc.CarDTO;
import edu.unsw.comp9321.jdbc.CarTypeDAO;
import edu.unsw.comp9321.jdbc.CarTypeDAOImpl;
import edu.unsw.comp9321.jdbc.CarTypeDTO;
import edu.unsw.comp9321.jdbc.CustomerTypeDAO;
import edu.unsw.comp9321.jdbc.CustomerTypeDAOImpl;
import edu.unsw.comp9321.jdbc.CustomerTypeDTO;
import edu.unsw.comp9321.jdbc.RestrictionDAO;
import edu.unsw.comp9321.jdbc.RestrictionDAOImpl;
import edu.unsw.comp9321.common.SearchBean;
import java.io.IOException;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import edu.unsw.comp9321.common.BookingBean;
import edu.unsw.comp9321.common.SearchBean;
import edu.unsw.comp9321.common.CarBean;
import edu.unsw.comp9321.common.DateIterator;
import edu.unsw.comp9321.common.ServiceLocatorException;
import edu.unsw.comp9321.exception.EmptyResultException;


public class Controller extends HttpServlet {

	private static final long serialVersionUID = 1L;
	public static final int ERROR = -1;
	public static final int NO_VIOLATION_CODE = -1;
	private static final String NO_VIOLATION = "noViolation";
	private static Logger logger = Logger.getLogger(Controller.class.getName());

	private static final String ACTION_KEY = "op";
	private static final String SEARCH_ACTION = "search";
	private static final String SEARCH_AGAIN_ACTION = "searchAgain";
	private static final String CHOOSE_CAR_ACTION = "chooseCar";
	private static final String CHECK_CUSTOMER_DETAILS_ACTION = "checkCustomerDetails";
	private static final String PAY_ACTION = "pay";
	private static final String VIEW_BOOKING_ACTION = "viewBooking";
	private static final String DELETE_BOOKING_ACTION = "deleteBooking";

	private static final String SEARCH_JSP = "/welcome.jsp";
	private static final String SEARCH_RESULT_JSP = "/searchResult.jsp";
	private static final String CUSTOMER_DETAILS_JSP = "/customerDetails.jsp";
	private static final String PAYMENT_DETAILS_JSP = "/paymentDetails.jsp";
	private static final String THANK_YOU_JSP = "/thankYou.jsp";
	private static final String VIEW_BOOKING_JSP = "/viewBooking.jsp";
	private static final String DELETE_BOOKING_JSP = "/deleteBooking.jsp";
	private static final String ERROR_JSP = "/error.jsp";
	

	private static final String VIOLATION_ATTR = "violation";
	private static final String NO_AVAILABLE_CAR_ERROR = "Sorry there were no available cars matching your criteria.";
	private static final String DATE_IN_FUTURE_ERROR = "Please enter a valid date. You cannot time travel.";
	private static final String DATE_FORMAT_ERROR = "Please enter a valid date. Please use YYYY-MM-DD format.";
	private static final String BOOKING_DATE_ERROR = "Please make sure that the end date is after the start date";
	private static final String NO_CAR_SELECTED_ERROR ="Please select a car.";
	private static final String NO_NAME_ERROR = "Please enter your name.";
	private static final String NO_EXCESS_ERROR = "Please choose whether you would like excess for the car.";
	private static final String NO_GREEN_OFFSET_ERROR = "Please choose whether you would like green offset for the car.";
	private static final String TOO_YOUNG_TO_DRIVE_ERROR = "You are too young to drive. Please find a person over 18 years of age to drive you.";
	private static final String LICENSE_LIMIT_FOR_MANUAL_ERROR = "You can ONLY rent an auto transmission car if your license is less than a year old.";
	private static final String RESTRICTION_VIOLATION_ERROR = "Sorry, there are restrictions on which cars you can rent.";
	private static final String CREDIT_CARD_ERROR = "Please enter a valid card number and CSV.";
	private static final String EXPIRY_DATE_ERROR = "Please enter a valid expiration date.";
	private static final String PIN_INSERT_ERROR = "Please enter a valid PIN number.";
	private static final String BOOKING_ID_INSERT_ERROR = "Sorry, it is not possible to retrieve your booking. Your URL seems wrong.";
	private static final String INVALID_EMAIL_ERROR = "Please enter a valid email.";
	private static final String VIEW_BOOKING_LESS_48HOURS_ERROR = "Sorry, you cannot check your booking details less than 48 hours before the booking's start date.";
	private static final String BOOKING_NOT_FOUND_ERROR = "Sorry, no booking has been found";
	private static final String NEW_BOOKING_OBJECT_NOT_FOUND_ERROR = "Sorry, an error occurred. Please start over again. ";


	private static final String MIDSIZE_GREEN = "Midsize Green"; // Disable green offset for this car.
	private static final long MILLISEC_IN_48HOURS = 172800000;

	private CarDAO carDAO;
	private CarTypeDAO carTypeDAO;
	private BookingDAO bookingDAO;
	private CustomerTypeDAO customerTypeDAO;
	private RestrictionDAO restrictionDAO;
	private String errorMsg;
	

	public Controller() throws ServletException {
		super();

		try {
			carDAO = new CarDAOImpl();
			carTypeDAO = new CarTypeDAOImpl();
			bookingDAO = new BookingDAOImpl();
			customerTypeDAO = new CustomerTypeDAOImpl();
			restrictionDAO = new RestrictionDAOImpl();
		} catch (ServiceLocatorException e) {
			logger.severe("Trouble connecting to database "+ e.getStackTrace());
			throw new ServletException();
		} catch (SQLException e) {
			logger.severe("Trouble connecting to database "+ e.getStackTrace());
			throw new ServletException();
		}
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		try {
			processRequest(request,response);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
		try {
			processRequest(request,response);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void processRequest(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException, ParseException, EmptyResultException {
		String actionName = request.getParameter(ACTION_KEY);
		if (actionName != null) {
			// Reset error message for every request.
			errorMsg = null;
			if (actionName.equals(SEARCH_ACTION)) {
				List<CarDTO> searchList = search(request, response);
				if (searchList.isEmpty() || errorMsg != null) {
					// No available cars OR an error has occurred. Go back to search page.
					System.out.println("ERROR: " + errorMsg);
					request.setAttribute(VIOLATION_ATTR, errorMsg);
					forward(request, response, SEARCH_JSP);
				} else {
					SearchBean searchResult = new SearchBean();
					for (CarDTO carDTO: searchList){
						CarBean carBean = new CarBean();
						carBean.setCar(carDTO);
						carBean.setCarType(carTypeDAO.findCarTypeWithId(String.valueOf(carDTO.getCarType())));
						searchResult.getCars().add(carBean);
					}
					request.setAttribute(VIOLATION_ATTR, NO_VIOLATION);
					request.getSession().setAttribute("searchResult", searchResult);
					forward(request, response, SEARCH_RESULT_JSP);
				}
			} else if (actionName.equals(SEARCH_AGAIN_ACTION)) {
				request.getSession().invalidate();
				forward(request, response, SEARCH_JSP);
			} else if (actionName.equals(CHOOSE_CAR_ACTION)) {
				String carIdStr = request.getParameter("chosenCar");
				if (carIdStr == null) {
					// Error. No car was chosen. Return to search result.
					System.out.println("ERROR: " + NO_CAR_SELECTED_ERROR);
					request.setAttribute(VIOLATION_ATTR, NO_CAR_SELECTED_ERROR);
					forward(request, response, SEARCH_RESULT_JSP);
				} else {
					int carId = Integer.valueOf(carIdStr);
					CarBean chosenCar = ((SearchBean) request.getSession().getAttribute("searchResult")).getCarBeanWithId(carId);
					double greenOffsetRate = chosenCar.getCarType().getDailyRate() * 1.02;
					request.getSession().setAttribute("greenOffsetRate", greenOffsetRate);
					request.setAttribute(VIOLATION_ATTR, NO_VIOLATION);
					request.getSession().setAttribute("chosenCar", chosenCar);
					forward(request, response, CUSTOMER_DETAILS_JSP);
				}
			} else if (actionName.equals(CHECK_CUSTOMER_DETAILS_ACTION)) {
				processCustomerDetails(request,response);
				if (errorMsg != null) {
					// Note: if there are multiple errors, the violation attribute will contain the most recently assigned one.
					System.out.println("ERROR: " + errorMsg);
					request.setAttribute(VIOLATION_ATTR, errorMsg);
					if (errorMsg.equals(LICENSE_LIMIT_FOR_MANUAL_ERROR) ||
							errorMsg.equals(RESTRICTION_VIOLATION_ERROR)) {
						// Error. Restrictions violated, return to search result.
						forward(request, response, SEARCH_RESULT_JSP);
					} else {
						// Error. Invalid input, return to customer details.
						forward(request, response, CUSTOMER_DETAILS_JSP);
					}
				} else {
					forward(request, response, PAYMENT_DETAILS_JSP);
				}
			} else if (actionName.equals(PAY_ACTION)) {
				processPaymentDetails(request, response);
				if (errorMsg != null) {
					System.out.println("ERROR: " + errorMsg);
					request.setAttribute(VIOLATION_ATTR, errorMsg);
					forward(request, response, PAYMENT_DETAILS_JSP);
				} else {
					makeBooking(request,response);
					if(errorMsg!=null){
						System.out.println("ERROR: " + errorMsg);
						request.setAttribute(VIOLATION_ATTR, errorMsg);
						forward(request, response, PAYMENT_DETAILS_JSP);
					} else {
						request.setAttribute(VIOLATION_ATTR, NO_VIOLATION);
						forward(request, response, THANK_YOU_JSP);
					}
				}
			} else if (actionName.equals(VIEW_BOOKING_ACTION)){
				checkBookingDetails(request,response);
				if (errorMsg != null) {
					System.out.println("ERROR: " + errorMsg);
					request.setAttribute(VIOLATION_ATTR, errorMsg);
					forward(request, response, VIEW_BOOKING_JSP);
				} else {
					request.setAttribute(VIOLATION_ATTR, NO_VIOLATION);
					forward(request, response, VIEW_BOOKING_JSP);
				}

			} else if (actionName.equals(DELETE_BOOKING_ACTION)){
				deleteBooking(request,response);
				if (errorMsg != null) {
					System.out.println("ERROR: " + errorMsg);
					request.setAttribute(VIOLATION_ATTR, errorMsg);
					forward(request, response, VIEW_BOOKING_JSP);
				} else {
					request.setAttribute(VIOLATION_ATTR, NO_VIOLATION);
					forward(request, response, DELETE_BOOKING_JSP);
				}
			} else {
				forward(request, response, ERROR_JSP);
			}
		}
	}

	private void makeBooking(HttpServletRequest request,
			HttpServletResponse response) {

		BookingDTO newBooking = (BookingDTO)request.getSession().getAttribute("newBooking");
		if (newBooking == null) {
			errorMsg = NEW_BOOKING_OBJECT_NOT_FOUND_ERROR;
			return;
		}

		if (newBooking.getCarId()<=0 || newBooking.getCustomerName()==null || newBooking.getStartDate()==null || newBooking.getEndDate()==null) {
			errorMsg = NEW_BOOKING_OBJECT_NOT_FOUND_ERROR;
			return;
		}
		BookingDTO booking = bookingDAO.insertBooking(newBooking.getCarId(), newBooking.getCustomerName(), newBooking.getStartDate(), newBooking.getEndDate());

		String customerEmail = (String)request.getSession().getAttribute("customerEmail");
		if(customerEmail==null || customerEmail.isEmpty()){
			errorMsg = INVALID_EMAIL_ERROR;
			return;
		}

		String url = "http://localhost:8080/CarRental/viewBooking.jsp?bookingId="+booking.getId();

		sendEmail(url,customerEmail,String.valueOf(booking.getCustomerPin()));

	}
	
	private void deleteBooking(HttpServletRequest request,
			HttpServletResponse response) {
		
		String bookingIdStr = request.getParameter("bookingId");
		int bookingId;
		try {

			bookingId = Integer.parseInt(bookingIdStr);
			if (bookingId <= 0) {
				errorMsg = BOOKING_ID_INSERT_ERROR;
				return;
			}
		} catch (NumberFormatException e) {
			System.out.println("ERROR: " + e.getMessage());
			errorMsg = BOOKING_ID_INSERT_ERROR;
			return;
		}
		BookingDTO booking = bookingDAO.findBookingWithId(bookingId);
		if(booking==null){
			errorMsg = BOOKING_NOT_FOUND_ERROR;
			return;
		}
		boolean isBookingDeleted = bookingDAO.deleteBooking(booking.getId());
		if(!isBookingDeleted){
			errorMsg = BOOKING_NOT_FOUND_ERROR;
			return;
		}
		
	}

	private void checkBookingDetails(HttpServletRequest request,
			HttpServletResponse response) {
		final int NUM_DIGITS_IN_PIN = 5;

		String bookingIdStr = request.getParameter("bookingId");
		String pinStr = request.getParameter("pin");
		int bookingId;
		int pin;
		try {

			bookingId = Integer.parseInt(bookingIdStr);
			if (bookingId <= 0) {
				errorMsg = BOOKING_ID_INSERT_ERROR;
				return;
			}
		} catch (NumberFormatException e) {
			System.out.println("ERROR: " + e.getMessage());
			errorMsg = BOOKING_ID_INSERT_ERROR;
			return;
		}
		try {
			pin = Integer.parseInt(pinStr);
//			System.out.println("bookingIdStr length " + bookingIdStr + " pin length " + pinStr.length());
			if (pin < 0 || pinStr.length() != NUM_DIGITS_IN_PIN ) {
				errorMsg = PIN_INSERT_ERROR;
				return;
			}
		} catch (NumberFormatException e) {
			System.out.println("ERROR: " + e.getMessage());
			errorMsg = PIN_INSERT_ERROR;
			return;
		}
//		System.out.println("bookingId = " + bookingId + ", pin = " + pin);

		BookingDTO booking = bookingDAO.findBookingWithId(bookingId);
		if(booking==null){
			errorMsg = BOOKING_NOT_FOUND_ERROR;
			return;
		}
		
		if(new Date().getTime()>booking.getStartDate().getTime() - MILLISEC_IN_48HOURS){
			errorMsg = VIEW_BOOKING_LESS_48HOURS_ERROR;
			return;
		}

		CarTypeDTO carType = carDAO.findCarTypeWithCarId(booking.getCarId());

		if(carType==null){
			errorMsg = BOOKING_NOT_FOUND_ERROR;
			return;
		}

		if(booking.getCustomerPin()!=pin){
			errorMsg = PIN_INSERT_ERROR;
			return;
		}
		BookingBean bookingBean = new BookingBean();
		bookingBean.setBooking(booking);
		bookingBean.setCarType(carType);
		request.getSession().setAttribute("booking", bookingBean);

	}

	private void processPaymentDetails(HttpServletRequest request,
			HttpServletResponse response) {

		final String EMAIL_REGEX = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}";
		final int NUM_DIGITS_IN_CCN = 16;
		final int NUM_DIGITS_IN_CSV = 3;
		final int MAX_NUM_DIGITS_IN_MONTH = 2;
		final int NUM_DIGITS_IN_YEAR = 4;

		String ccnStr = request.getParameter("creditCardNumber");
		String csvStr = request.getParameter("creditCardCsv");
		long ccn;
		int csv;
		try {
			ccn = Long.parseLong(ccnStr);
			csv = Integer.parseInt(csvStr);
//			System.out.println("csv length " + csvStr.length() + " ccn length " + ccnStr.length());
			if (ccn < 0 || csv < 0 || ccnStr.length() != NUM_DIGITS_IN_CCN ||
					csvStr.length() != NUM_DIGITS_IN_CSV) {
				errorMsg = CREDIT_CARD_ERROR;
				return;
			}
		} catch (NumberFormatException e) {
			System.out.println("ERROR: " + e.getMessage());
			errorMsg = CREDIT_CARD_ERROR;
			return;
		}
//		System.out.println("ccn = " + ccn + ", csv = " + csv);

		String monthStr = request.getParameter("expiryMonth");
		String yearStr = request.getParameter("expiryYear");
		int expiryMonth, expiryYear;
		try {
			expiryMonth = Integer.parseInt(monthStr);
			expiryYear = Integer.parseInt(yearStr);
			if (expiryMonth < 0 || expiryYear < 0 || monthStr.length() > MAX_NUM_DIGITS_IN_MONTH ||
					yearStr.length() != NUM_DIGITS_IN_YEAR) {
				errorMsg = EXPIRY_DATE_ERROR;
				return;
			}
		} catch (NumberFormatException e) {
			errorMsg = EXPIRY_DATE_ERROR;
			return;
		}

		int currentYear =  Integer.parseInt(new java.text.SimpleDateFormat("yyyy").format(new java.util.Date()));
//		System.out.println("current year = " + currentYear + ", expiryMonth = " + expiryMonth + ", expiryYear = " + expiryYear);
		if (expiryYear - currentYear < 0) {
			errorMsg = EXPIRY_DATE_ERROR;
			return;
		}

		String email = request.getParameter("customerEmail");
		if (email == null || email.isEmpty() || !email.matches(EMAIL_REGEX)) {
			errorMsg = INVALID_EMAIL_ERROR;
			return;
		}
		request.getSession().setAttribute("customerEmail", email);
	}

	private List<CarDTO> search(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException, EmptyResultException {

		SimpleDateFormat parseDate = new java.text.SimpleDateFormat("yyyy-MM-dd");

		String startDateStr = request.getParameter("startDate");
		String endDateStr = request.getParameter("endDate");
		String bodyType = request.getParameter("bodyType");
		String transType = request.getParameter("transType");
		boolean isAutomatic = transType.equals("Automatic") ? true : false;

		List<Integer> availableCars = new ArrayList<Integer>();
		List<CarDTO> wantedCars = new ArrayList<CarDTO>();
		List<CarDTO> finalCars = new ArrayList<CarDTO>(); 

		Date startDate;
		Date endDate;
		final int dayInMs = 1000 * 60 * 60 * 24;
		// TDOO: Fix bug where enddate of 1212-12-1212 will work.
		try {
			startDate = (Date) parseDate.parse(startDateStr);
			endDate = (Date) parseDate.parse(endDateStr);
		} catch (ParseException e) {
			errorMsg = DATE_FORMAT_ERROR;
			return finalCars;
		}



		// Number of days to rent includes the start date itself.
		int numDaysToRent = (int) ((endDate.getTime() - startDate.getTime())/dayInMs) + 1;
		if (numDaysToRent < 0) {
			errorMsg = BOOKING_DATE_ERROR;
			return finalCars;
		}
		request.getSession().setAttribute("numDaysToRent", numDaysToRent);

		wantedCars = carDAO.findCars(bodyType, isAutomatic);

		// Assume all cars that match the customer's criteria is available.
		for (CarDTO car: wantedCars) {
			availableCars.add(car.getCarId());
		}

		// Get all bookings with wanted car IDs.
		List<BookingDTO> bookingsList = bookingDAO.findBookingsWithCarIds(availableCars);

		for (BookingDTO booking: bookingsList) {
			DateIterator chosenDates = new DateIterator(startDate, endDate);
			boolean clashFound = false;

			dateLoop:
				for (Date date: chosenDates) {
					if (isWithinRange(date,booking.getStartDate(),booking.getEndDate())) {
						clashFound = true;
						break dateLoop;
					}
				}

			int bookedCarId = booking.getCarId();
			if (clashFound && availableCars.contains(bookedCarId)) {
				// If there is a clash, remove that carId from available car id list.
				availableCars.remove(new Integer(bookedCarId));
//				System.out.println("Removing this car, clash found");
			}
		}

		// Add the corresponding CarDTO object for all available cars.
		for (CarDTO wantedCar: wantedCars) {
			if (availableCars.contains(wantedCar.getCarId())) {
				finalCars.add(wantedCar);
			}
		}

		if (finalCars.isEmpty()) {
			errorMsg = NO_AVAILABLE_CAR_ERROR;
			return finalCars;
		}

		BookingDTO newBooking = new BookingDTO();
		newBooking.setStartDate(startDate);
		newBooking.setEndDate(endDate);
		request.getSession().setAttribute("newBooking", newBooking);
		return finalCars;
	}

	private void forward(HttpServletRequest request, HttpServletResponse response, String jspFile) throws ServletException, IOException {
		RequestDispatcher rd = request.getRequestDispatcher(jspFile);
		rd.forward(request, response);
	}

	boolean isWithinRange(Date testDate, Date startDate,Date endDate) {
		return !(testDate.before(startDate) || testDate.after(endDate));
	}

	private void processCustomerDetails(HttpServletRequest request, HttpServletResponse response) 
			throws  ServletException, IOException, EmptyResultException {

		SimpleDateFormat parseDate = new java.text.SimpleDateFormat("yyyy-MM-dd");

		String dateOfBirthStr = request.getParameter("dateOfBirth");
		String licenseDateStr = request.getParameter("licenseDate");

		Date dateOfBirth;
		Date licenseDate;

		try {
			dateOfBirth = (Date) parseDate.parse(dateOfBirthStr);
			licenseDate = (Date) parseDate.parse(licenseDateStr);
		} catch (ParseException e) {
			errorMsg = DATE_FORMAT_ERROR;
			return;
		}

		int customerAge = getYearsGapToNow(dateOfBirth);
		int licenseDuration = getYearsGapToNow(licenseDate);
		if (customerAge == ERROR || licenseDuration == ERROR) {
			errorMsg = DATE_IN_FUTURE_ERROR;
			return;
		}

		String customerName = request.getParameter("customerName");
		String excess = request.getParameter("excess");
		String greenOffset = request.getParameter("greenOffset");

		if (customerName == null || customerName.isEmpty()) {
			errorMsg = NO_NAME_ERROR;
			return;
		}
		if (excess == null || excess.isEmpty()) {
			errorMsg = NO_EXCESS_ERROR;
			return;
		}

		// Note: greenOffset parameter will be null anyway if car is Midsize Green.
		CarBean chosenCar = (CarBean) request.getSession().getAttribute("chosenCar");
		CarTypeDTO carType = chosenCar.getCarType();
		if (greenOffset == null && !carType.getTypeClass().equals(MIDSIZE_GREEN)) {
			errorMsg = NO_GREEN_OFFSET_ERROR;
			return;
		}

		CustomerTypeDTO customerType = customerTypeDAO.findCustomerType(customerAge, licenseDuration); 
		if (customerType == null) {
			// Assuming the database is correct, if the query returns null we can deduce
			// that the customer is too young to drive i.e. their age is less than 18.
			errorMsg = TOO_YOUNG_TO_DRIVE_ERROR;
			return;
		}

		// A customer cannot drive a manual car if their license duration is less than one year.
		if (licenseDuration < 1 && carType.isAutomatic() == false) {
			errorMsg = LICENSE_LIMIT_FOR_MANUAL_ERROR;
			return;
		}

		DecimalFormat df = new DecimalFormat("########.00");
		int numDaysToRent = (Integer) request.getSession().getAttribute("numDaysToRent");
		double dailyRate = carType.getDailyRate();
		final double EXCESS_COST = 150;

		if (greenOffset.equals("yes")) {
			dailyRate = 1.02 * dailyRate;
		}

		double totalFee = dailyRate * numDaysToRent;

		double excessAmnt = customerType.getInsuranceExcess();

		int additional_excess = restrictionDAO.findRestrictionAndExcess(customerType.getCustomerTypeId(), carType.getId());
		if (additional_excess != NO_VIOLATION_CODE) {
			if (additional_excess == 0) {
				errorMsg = RESTRICTION_VIOLATION_ERROR;
				return;
			} else {
				excessAmnt = excessAmnt + additional_excess;
//				System.out.println("This order incurred an additional $" + additional_excess  +" excess amount. New excess is " + excessAmnt);
			}
		}

		if (excess.equals("yes")) {
			excessAmnt = excessAmnt / 2;
			totalFee = totalFee + EXCESS_COST;
		} else {
			excessAmnt = customerType.getInsuranceExcess();
		}
		BookingDTO newBooking = (BookingDTO)request.getSession().getAttribute("newBooking");
		if (newBooking == null) {
			errorMsg = NEW_BOOKING_OBJECT_NOT_FOUND_ERROR;
			return;
		}
		newBooking.setCustomerName(customerName);
		newBooking.setCarId(chosenCar.getCar().getCarId());
//		System.out.println("total fee = " + totalFee + ", excessAmnt = " + excessAmnt);
		request.getSession().setAttribute("totalFee", df.format(totalFee));
		request.getSession().setAttribute("excessAmnt", df.format(excessAmnt));
	}

	public int getYearsGapToNow(Date date) {
		int yearsGap = 0;
		Calendar today = Calendar.getInstance();
		Calendar dateToCheck = Calendar.getInstance();
		dateToCheck.setTime(date);

		if (dateToCheck.after(today)) {
			yearsGap = ERROR;
		}

		if (yearsGap != ERROR) {
			yearsGap = today.get(Calendar.YEAR) - dateToCheck.get(Calendar.YEAR);
			// If birth date is greater than todays date (after 2 days adjustment of leap year) then decrement age one year   
			if ((dateToCheck.get(Calendar.DAY_OF_YEAR) - today.get(Calendar.DAY_OF_YEAR) > 3) ||
					(dateToCheck.get(Calendar.MONTH) > today.get(Calendar.MONTH ))) {
				yearsGap--;
				// If birth date and todays date are of same month and birth day of month is greater than todays day of month then decrement age
			} else if ((dateToCheck.get(Calendar.MONTH) == today.get(Calendar.MONTH )) &&
					(dateToCheck.get(Calendar.DAY_OF_MONTH) > today.get(Calendar.DAY_OF_MONTH ))){
				yearsGap--;
			}
		}
		return yearsGap;
	}

	//	For e.g. sendEmail("www.google.com.au", "marco.pricone@gmail.com");
	private void sendEmail(String url, String toEmail, String customerPin) {
		Properties props = new Properties();
		props.put("mail.transport.protocol", "smtp");
		props.put("mail.smtp.host", "smtp.gmail.com");
		props.put("mail.smtp.port", "465");
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.starttls.enable", "true");
		props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
		Session s = Session.getInstance(props,  
				new javax.mail.Authenticator() {  
			protected PasswordAuthentication getPasswordAuthentication() {  
				return new PasswordAuthentication("nottyredofrentingcars@gmail.com","carsAreAwesome");  
			}
		});

		try {
			MimeMessage message = new MimeMessage(s);
			InternetAddress from = new InternetAddress("support@nottyredofrentingcars.com");
			message.setFrom(from);
			InternetAddress to = new InternetAddress(toEmail);
			message.addRecipient(Message.RecipientType.TO, to);
			message.setSubject("[Automated Email] Your Car Reservation");
			message.setText("Please check your car booking within 48hrs. " +
					"Click link here: " + url + " \r\n\r\n Your PIN is: "+customerPin);
			Transport.send(message);
//			System.out.println("Email sent!");
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("Exception: " + e.getMessage());
		}
	}

}
